home *** CD-ROM | disk | FTP | other *** search
/ Freelog 125 / Freelog_MarsAvril2015_No125.iso / ViePratique / ArchiFacile / ArchiFacileSetup.exe / {app} / nw.pak / Unnamed File 000117.txt < prev    next >
Text File  |  2014-10-14  |  24KB  |  681 lines

  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. /**
  6.  * @fileoverview Card slider implementation. Allows you to create interactions
  7.  * that have items that can slide left to right to reveal additional items.
  8.  * Works by adding the necessary event handlers to a specific DOM structure
  9.  * including a frame, container and cards.
  10.  * - The frame defines the boundary of one item. Each card will be expanded to
  11.  *   fill the width of the frame. This element is also overflow hidden so that
  12.  *   the additional items left / right do not trigger horizontal scrolling.
  13.  * - The container is what all the touch events are attached to. This element
  14.  *   will be expanded to be the width of all cards.
  15.  * - The cards are the individual viewable items. There should be one card for
  16.  *   each item in the list. Only one card will be visible at a time. Two cards
  17.  *   will be visible while you are transitioning between cards.
  18.  *
  19.  * This class is designed to work well on any hardware-accelerated touch device.
  20.  * It should still work on pre-hardware accelerated devices it just won't feel
  21.  * very good. It should also work well with a mouse.
  22.  */
  23.  
  24. // Use an anonymous function to enable strict mode just for this file (which
  25. // will be concatenated with other files when embedded in Chrome
  26. cr.define('cr.ui', function() {
  27.   'use strict';
  28.  
  29.   /**
  30.    * @constructor
  31.    * @param {!Element} frame The bounding rectangle that cards are visible in.
  32.    * @param {!Element} container The surrounding element that will have event
  33.    *     listeners attached to it.
  34.    * @param {number} cardWidth The width of each card should have.
  35.    */
  36.   function CardSlider(frame, container, cardWidth) {
  37.     /**
  38.      * @type {!Element}
  39.      * @private
  40.      */
  41.     this.frame_ = frame;
  42.  
  43.     /**
  44.      * @type {!Element}
  45.      * @private
  46.      */
  47.     this.container_ = container;
  48.  
  49.     /**
  50.      * Array of card elements.
  51.      * @type {!Array.<!Element>}
  52.      * @private
  53.      */
  54.     this.cards_ = [];
  55.  
  56.     /**
  57.      * Index of currently shown card.
  58.      * @type {number}
  59.      * @private
  60.      */
  61.     this.currentCard_ = -1;
  62.  
  63.     /**
  64.      * @type {number}
  65.      * @private
  66.      */
  67.     this.cardWidth_ = cardWidth;
  68.  
  69.     /**
  70.      * @type {!cr.ui.TouchHandler}
  71.      * @private
  72.      */
  73.     this.touchHandler_ = new cr.ui.TouchHandler(this.container_);
  74.   }
  75.  
  76.  
  77.   /**
  78.    * The time to transition between cards when animating. Measured in ms.
  79.    * @type {number}
  80.    * @private
  81.    * @const
  82.    */
  83.   CardSlider.TRANSITION_TIME_ = 200;
  84.  
  85.  
  86.   /**
  87.    * The minimum velocity required to transition cards if they did not drag past
  88.    * the halfway point between cards. Measured in pixels / ms.
  89.    * @type {number}
  90.    * @private
  91.    * @const
  92.    */
  93.   CardSlider.TRANSITION_VELOCITY_THRESHOLD_ = 0.2;
  94.  
  95.  
  96.   CardSlider.prototype = {
  97.     /**
  98.      * The current left offset of the container relative to the frame. This
  99.      * position does not include deltas from active drag operations, and
  100.      * always aligns with a frame boundary.
  101.      * @type {number}
  102.      * @private
  103.      */
  104.     currentLeft_: 0,
  105.  
  106.     /**
  107.      * Current offset relative to |currentLeft_| due to an active drag
  108.      * operation.
  109.      * @type {number}
  110.      * @private
  111.      */
  112.     deltaX_: 0,
  113.  
  114.     /**
  115.      * Initialize all elements and event handlers. Must call after construction
  116.      * and before usage.
  117.      * @param {boolean} ignoreMouseWheelEvents If true, horizontal mouse wheel
  118.      *     events will be ignored, rather than flipping between pages.
  119.      */
  120.     initialize: function(ignoreMouseWheelEvents) {
  121.       var view = this.container_.ownerDocument.defaultView;
  122.       assert(view.getComputedStyle(this.container_).display == '-webkit-box',
  123.           'Container should be display -webkit-box.');
  124.       assert(view.getComputedStyle(this.frame_).overflow == 'hidden',
  125.           'Frame should be overflow hidden.');
  126.       assert(view.getComputedStyle(this.container_).position == 'static',
  127.           'Container should be position static.');
  128.  
  129.       this.updateCardWidths_();
  130.  
  131.       this.mouseWheelScrollAmount_ = 0;
  132.       this.mouseWheelCardSelected_ = false;
  133.       this.mouseWheelIsContinuous_ = false;
  134.       this.scrollClearTimeout_ = null;
  135.       if (!ignoreMouseWheelEvents) {
  136.         this.frame_.addEventListener('mousewheel',
  137.                                      this.onMouseWheel_.bind(this));
  138.       }
  139.       this.container_.addEventListener(
  140.           'webkitTransitionEnd', this.onWebkitTransitionEnd_.bind(this));
  141.  
  142.       // Also support touch events in case a touch screen happens to be
  143.       // available.  Note that this has minimal impact in the common case of
  144.       // no touch events (eg. we're mainly just adding listeners for events that
  145.       // will never trigger).
  146.       var TouchHandler = cr.ui.TouchHandler;
  147.       this.container_.addEventListener(TouchHandler.EventType.TOUCH_START,
  148.                                        this.onTouchStart_.bind(this));
  149.       this.container_.addEventListener(TouchHandler.EventType.DRAG_START,
  150.                                        this.onDragStart_.bind(this));
  151.       this.container_.addEventListener(TouchHandler.EventType.DRAG_MOVE,
  152.                                        this.onDragMove_.bind(this));
  153.       this.container_.addEventListener(TouchHandler.EventType.DRAG_END,
  154.                                        this.onDragEnd_.bind(this));
  155.  
  156.       this.touchHandler_.enable(/* opt_capture */ false);
  157.     },
  158.  
  159.     /**
  160.      * Use in cases where the width of the frame has changed in order to update
  161.      * the width of cards. For example should be used when orientation changes
  162.      * in full width sliders.
  163.      * @param {number} newCardWidth Width all cards should have, in pixels.
  164.      */
  165.     resize: function(newCardWidth) {
  166.       if (newCardWidth != this.cardWidth_) {
  167.         this.cardWidth_ = newCardWidth;
  168.  
  169.         this.updateCardWidths_();
  170.  
  171.         // Must upate the transform on the container to show the correct card.
  172.         this.transformToCurrentCard_();
  173.       }
  174.     },
  175.  
  176.     /**
  177.      * Sets the cards used. Can be called more than once to switch card sets.
  178.      * @param {!Array.<!Element>} cards The individual viewable cards.
  179.      * @param {number} index Index of the card to in the new set of cards to
  180.      *     navigate to.
  181.      */
  182.     setCards: function(cards, index) {
  183.       assert(index >= 0 && index < cards.length,
  184.           'Invalid index in CardSlider#setCards');
  185.       this.cards_ = cards;
  186.  
  187.       this.updateCardWidths_();
  188.       this.updateSelectedCardAttributes_();
  189.  
  190.       // Jump to the given card index.
  191.       this.selectCard(index, false, false, true);
  192.     },
  193.  
  194.     /**
  195.      * Ensures that for all cards:
  196.      * - if the card is the current card, then it has 'selected-card' in its
  197.      *   classList, and is visible for accessibility
  198.      * - if the card is not the selected card, then it does not have
  199.      *   'selected-card' in its classList, and is invisible for accessibility.
  200.      * @private
  201.      */
  202.     updateSelectedCardAttributes_: function() {
  203.       for (var i = 0; i < this.cards_.length; i++) {
  204.         if (i == this.currentCard_) {
  205.           this.cards_[i].classList.add('selected-card');
  206.           this.cards_[i].removeAttribute('aria-hidden');
  207.         } else {
  208.           this.cards_[i].classList.remove('selected-card');
  209.           this.cards_[i].setAttribute('aria-hidden', true);
  210.         }
  211.       }
  212.     },
  213.  
  214.     /**
  215.      * Updates the width of each card.
  216.      * @private
  217.      */
  218.     updateCardWidths_: function() {
  219.       for (var i = 0, card; card = this.cards_[i]; i++)
  220.         card.style.width = this.cardWidth_ + 'px';
  221.     },
  222.  
  223.     /**
  224.      * Returns the index of the current card.
  225.      * @return {number} index of the current card.
  226.      */
  227.     get currentCard() {
  228.       return this.currentCard_;
  229.     },
  230.  
  231.     /**
  232.      * Allows setting the current card index.
  233.      * @param {number} index A new index to set the current index to.
  234.      * @return {number} The new index after having been set.
  235.      */
  236.     set currentCard(index) {
  237.       return (this.currentCard_ = index);
  238.     },
  239.  
  240.     /**
  241.      * Returns the number of cards.
  242.      * @return {number} number of cards.
  243.      */
  244.     get cardCount() {
  245.       return this.cards_.length;
  246.     },
  247.  
  248.     /**
  249.      * Returns the current card itself.
  250.      * @return {!Element} the currently shown card.
  251.      */
  252.     get currentCardValue() {
  253.       return this.cards_[this.currentCard_];
  254.     },
  255.  
  256.     /**
  257.      * Returns the frame holding the cards.
  258.      * @return {Element} The frame used to position the cards.
  259.      */
  260.     get frame() {
  261.       return this.frame_;
  262.     },
  263.  
  264.     /**
  265.      * Handle horizontal scrolls to flip between pages.
  266.      * @private
  267.      */
  268.     onMouseWheel_: function(e) {
  269.       if (e.wheelDeltaX == 0)
  270.         return;
  271.  
  272.       // Continuous devices such as an Apple Touchpad or Apple MagicMouse will
  273.       // send arbitrary delta values. Conversly, standard mousewheels will
  274.       // send delta values in increments of 120.  (There is of course a small
  275.       // chance we mistake a continuous device for a non-continuous device.
  276.       // Unfortunately there isn't a better way to do this until real touch
  277.       // events are available to desktop clients.)
  278.       var DISCRETE_DELTA = 120;
  279.       if (e.wheelDeltaX % DISCRETE_DELTA)
  280.         this.mouseWheelIsContinuous_ = true;
  281.  
  282.       if (this.mouseWheelIsContinuous_) {
  283.         // For continuous devices, detect a page swipe when the accumulated
  284.         // delta matches a pre-defined threshhold.  After changing the page,
  285.         // ignore wheel events for a short time before repeating this process.
  286.         if (this.mouseWheelCardSelected_) return;
  287.         this.mouseWheelScrollAmount_ += e.wheelDeltaX;
  288.         if (Math.abs(this.mouseWheelScrollAmount_) >= 600) {
  289.           var pagesToScroll = this.mouseWheelScrollAmount_ > 0 ? 1 : -1;
  290.           if (!isRTL())
  291.             pagesToScroll *= -1;
  292.           var newCardIndex = this.currentCard + pagesToScroll;
  293.           newCardIndex = Math.min(this.cards_.length - 1,
  294.                                   Math.max(0, newCardIndex));
  295.           this.selectCard(newCardIndex, true);
  296.           this.mouseWheelCardSelected_ = true;
  297.         }
  298.       } else {
  299.         // For discrete devices, consider each wheel tick a page change.
  300.         var pagesToScroll = e.wheelDeltaX / DISCRETE_DELTA;
  301.         if (!isRTL())
  302.           pagesToScroll *= -1;
  303.         var newCardIndex = this.currentCard + pagesToScroll;
  304.         newCardIndex = Math.min(this.cards_.length - 1,
  305.                                 Math.max(0, newCardIndex));
  306.         this.selectCard(newCardIndex, true);
  307.       }
  308.  
  309.       // We got a mouse wheel event, so cancel any pending scroll wheel timeout.
  310.       if (this.scrollClearTimeout_ != null)
  311.         clearTimeout(this.scrollClearTimeout_);
  312.       // If we didn't use up all the scroll, hold onto it for a little bit, but
  313.       // drop it after a delay.
  314.       if (this.mouseWheelScrollAmount_ != 0) {
  315.         this.scrollClearTimeout_ =
  316.             setTimeout(this.clearMouseWheelScroll_.bind(this), 500);
  317.       }
  318.     },
  319.  
  320.     /**
  321.      * Resets the amount of horizontal scroll we've seen to 0. See
  322.      * onMouseWheel_.
  323.      * @private
  324.      */
  325.     clearMouseWheelScroll_: function() {
  326.       this.mouseWheelScrollAmount_ = 0;
  327.       this.mouseWheelCardSelected_ = false;
  328.     },
  329.  
  330.     /**
  331.      * Handles the ends of -webkit-transitions on -webkit-transform (animated
  332.      * card switches).
  333.      * @param {Event} e The webkitTransitionEnd event.
  334.      * @private
  335.      */
  336.     onWebkitTransitionEnd_: function(e) {
  337.       // Ignore irrelevant transitions that might bubble up.
  338.       if (e.target !== this.container_ ||
  339.           e.propertyName != '-webkit-transform') {
  340.         return;
  341.       }
  342.       this.fireChangeEndedEvent_(true);
  343.     },
  344.  
  345.     /**
  346.      * Dispatches a simple event to tell subscribers we're done moving to the
  347.      * newly selected card.
  348.      * @param {boolean} wasAnimated whether or not the change was animated.
  349.      * @private
  350.      */
  351.     fireChangeEndedEvent_: function(wasAnimated) {
  352.       var e = document.createEvent('Event');
  353.       e.initEvent('cardSlider:card_change_ended', true, true);
  354.       e.cardSlider = this;
  355.       e.changedTo = this.currentCard_;
  356.       e.wasAnimated = wasAnimated;
  357.       this.container_.dispatchEvent(e);
  358.     },
  359.  
  360.     /**
  361.      * Add a card to the card slider at a particular index. If the card being
  362.      * added is inserted in front of the current card, cardSlider.currentCard
  363.      * will be adjusted accordingly (to current card + 1).
  364.      * @param {!Node} card A card that will be added to the card slider.
  365.      * @param {number} index An index at which the given |card| should be
  366.      *     inserted. Must be positive and less than the number of cards.
  367.      */
  368.     addCardAtIndex: function(card, index) {
  369.       assert(card instanceof Node, '|card| isn\'t a Node');
  370.       this.assertValidIndex_(index);
  371.       this.cards_ = Array.prototype.concat.call(
  372.           this.cards_.slice(0, index), card, this.cards_.slice(index));
  373.  
  374.       this.updateSelectedCardAttributes_();
  375.  
  376.       if (this.currentCard_ == -1)
  377.         this.currentCard_ = 0;
  378.       else if (index <= this.currentCard_)
  379.         this.selectCard(this.currentCard_ + 1, false, true, true);
  380.  
  381.       this.fireAddedEvent_(card, index);
  382.     },
  383.  
  384.     /**
  385.      * Append a card to the end of the list.
  386.      * @param {!Node} card A card to add at the end of the card slider.
  387.      */
  388.     appendCard: function(card) {
  389.       assert(card instanceof Node, '|card| isn\'t a Node');
  390.       this.cards_.push(card);
  391.       this.fireAddedEvent_(card, this.cards_.length - 1);
  392.     },
  393.  
  394.     /**
  395.      * Dispatches a simple event to tell interested subscribers that a card was
  396.      * added to this card slider.
  397.      * @param {Node} card The recently added card.
  398.      * @param {number} index The position of the newly added card.
  399.      * @private
  400.      */
  401.     fireAddedEvent_: function(card, index) {
  402.       this.assertValidIndex_(index);
  403.       var e = document.createEvent('Event');
  404.       e.initEvent('cardSlider:card_added', true, true);
  405.       e.addedIndex = index;
  406.       e.addedCard = card;
  407.       this.container_.dispatchEvent(e);
  408.     },
  409.  
  410.     /**
  411.      * Returns the card at a particular index.
  412.      * @param {number} index The index of the card to return.
  413.      * @return {!Element} The card at the given index.
  414.      */
  415.     getCardAtIndex: function(index) {
  416.       this.assertValidIndex_(index);
  417.       return this.cards_[index];
  418.     },
  419.  
  420.     /**
  421.      * Removes a card by index from the card slider. If the card to be removed
  422.      * is the current card or in front of the current card, the current card
  423.      * will be updated (to current card - 1).
  424.      * @param {!Node} card A card to be removed.
  425.      */
  426.     removeCard: function(card) {
  427.       assert(card instanceof Node, '|card| isn\'t a Node');
  428.       this.removeCardAtIndex(this.cards_.indexOf(card));
  429.     },
  430.  
  431.     /**
  432.      * Removes a card by index from the card slider. If the card to be removed
  433.      * is the current card or in front of the current card, the current card
  434.      * will be updated (to current card - 1).
  435.      * @param {number} index The index of the tile that should be removed.
  436.      */
  437.     removeCardAtIndex: function(index) {
  438.       this.assertValidIndex_(index);
  439.       var removed = this.cards_.splice(index, 1).pop();
  440.  
  441.       if (this.cards_.length == 0)
  442.         this.currentCard_ = -1;
  443.       else if (index < this.currentCard_)
  444.         this.selectCard(this.currentCard_ - 1, false, true);
  445.  
  446.       this.fireRemovedEvent_(removed, index);
  447.     },
  448.  
  449.     /**
  450.      * Dispatches a cardSlider:card_removed event so interested subscribers know
  451.      * when a card was removed from this card slider.
  452.      * @param {Node} card The recently removed card.
  453.      * @param {number} index The index of the card before it was removed.
  454.      * @private
  455.      */
  456.     fireRemovedEvent_: function(card, index) {
  457.       var e = document.createEvent('Event');
  458.       e.initEvent('cardSlider:card_removed', true, true);
  459.       e.removedCard = card;
  460.       e.removedIndex = index;
  461.       this.container_.dispatchEvent(e);
  462.     },
  463.  
  464.     /**
  465.      * This re-syncs the -webkit-transform that's used to position the frame in
  466.      * the likely event it needs to be updated by a card being inserted or
  467.      * removed in the flow.
  468.      */
  469.     repositionFrame: function() {
  470.       this.transformToCurrentCard_();
  471.     },
  472.  
  473.     /**
  474.      * Checks the the given |index| exists in this.cards_.
  475.      * @param {number} index An index to check.
  476.      * @private
  477.      */
  478.     assertValidIndex_: function(index) {
  479.       assert(index >= 0 && index < this.cards_.length);
  480.     },
  481.  
  482.     /**
  483.      * Selects a new card, ensuring that it is a valid index, transforming the
  484.      * view and possibly calling the change card callback.
  485.      * @param {number} newCardIndex Index of card to show.
  486.      * @param {boolean=} opt_animate If true will animate transition from
  487.      *     current position to new position.
  488.      * @param {boolean=} opt_dontNotify If true, don't tell subscribers that
  489.      *     we've changed cards.
  490.      * @param {boolean=} opt_forceChange If true, ignore if the card already
  491.      *     selected.
  492.      */
  493.     selectCard: function(newCardIndex,
  494.                          opt_animate,
  495.                          opt_dontNotify,
  496.                          opt_forceChange) {
  497.       this.assertValidIndex_(newCardIndex);
  498.  
  499.       var previousCard = this.currentCardValue;
  500.       var isChangingCard =
  501.           !this.cards_[newCardIndex].classList.contains('selected-card');
  502.  
  503.       if (typeof opt_forceChange != 'undefined' && opt_forceChange)
  504.         isChangingCard = true;
  505.  
  506.       if (isChangingCard) {
  507.         this.currentCard_ = newCardIndex;
  508.         this.updateSelectedCardAttributes_();
  509.       }
  510.  
  511.       var willTransitionHappen = this.transformToCurrentCard_(opt_animate);
  512.  
  513.       if (isChangingCard && !opt_dontNotify) {
  514.         var event = document.createEvent('Event');
  515.         event.initEvent('cardSlider:card_changed', true, true);
  516.         event.cardSlider = this;
  517.         event.wasAnimated = !!opt_animate;
  518.         this.container_.dispatchEvent(event);
  519.  
  520.         // We also dispatch an event on the cards themselves.
  521.         if (previousCard) {
  522.           cr.dispatchSimpleEvent(previousCard, 'carddeselected',
  523.                                  true, true);
  524.         }
  525.         cr.dispatchSimpleEvent(this.currentCardValue, 'cardselected',
  526.                                true, true);
  527.       }
  528.  
  529.       // If we're not changing, animated, or transitioning, fire a
  530.       // cardSlider:card_change_ended event right away.
  531.       if ((!isChangingCard || !opt_animate || !willTransitionHappen) &&
  532.           !opt_dontNotify) {
  533.         this.fireChangeEndedEvent_(false);
  534.       }
  535.     },
  536.  
  537.     /**
  538.      * Selects a card from the stack. Passes through to selectCard.
  539.      * @param {Node} newCard The card that should be selected.
  540.      * @param {boolean=} opt_animate Whether to animate.
  541.      */
  542.     selectCardByValue: function(newCard, opt_animate) {
  543.       var i = this.cards_.indexOf(newCard);
  544.       assert(i != -1);
  545.       this.selectCard(i, opt_animate);
  546.     },
  547.  
  548.     /**
  549.      * Centers the view on the card denoted by this.currentCard. Can either
  550.      * animate to that card or snap to it.
  551.      * @param {boolean=} opt_animate If true will animate transition from
  552.      *     current position to new position.
  553.      * @return {boolean} Whether or not a transformation was necessary.
  554.      * @private
  555.      */
  556.     transformToCurrentCard_: function(opt_animate) {
  557.       var prevLeft = this.currentLeft_;
  558.       this.currentLeft_ = -this.cardWidth_ *
  559.           (isRTL() ? this.cards_.length - this.currentCard - 1 :
  560.                      this.currentCard);
  561.  
  562.       // If there's no change, return something to let the caller know there
  563.       // won't be a transition occuring.
  564.       if (prevLeft == this.currentLeft_ && this.deltaX_ == 0)
  565.         return false;
  566.  
  567.       // Animate to the current card, which will either transition if the
  568.       // current card is new, or reset the existing card if we didn't drag
  569.       // enough to change cards.
  570.       var transition = '';
  571.       if (opt_animate) {
  572.         transition = '-webkit-transform ' + CardSlider.TRANSITION_TIME_ +
  573.                      'ms ease-in-out';
  574.       }
  575.       this.container_.style.WebkitTransition = transition;
  576.       this.translateTo_(this.currentLeft_);
  577.  
  578.       return true;
  579.     },
  580.  
  581.     /**
  582.      * Moves the view to the specified position.
  583.      * @param {number} x Horizontal position to move to.
  584.      * @private
  585.      */
  586.     translateTo_: function(x) {
  587.       // We use a webkitTransform to slide because this is GPU accelerated on
  588.       // Chrome and iOS.  Once Chrome does GPU acceleration on the position
  589.       // fixed-layout elements we could simply set the element's position to
  590.       // fixed and modify 'left' instead.
  591.       this.deltaX_ = x - this.currentLeft_;
  592.       this.container_.style.WebkitTransform = 'translate3d(' + x + 'px, 0, 0)';
  593.     },
  594.  
  595.     /* Touch ******************************************************************/
  596.  
  597.     /**
  598.      * Clear any transition that is in progress and enable dragging for the
  599.      * touch.
  600.      * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event.
  601.      * @private
  602.      */
  603.     onTouchStart_: function(e) {
  604.       this.container_.style.WebkitTransition = '';
  605.       e.enableDrag = true;
  606.     },
  607.  
  608.     /**
  609.      * Tell the TouchHandler that dragging is acceptable when the user begins by
  610.      * scrolling horizontally and there is more than one card to slide.
  611.      * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event.
  612.      * @private
  613.      */
  614.     onDragStart_: function(e) {
  615.       e.enableDrag = this.cardCount > 1 && Math.abs(e.dragDeltaX) >
  616.           Math.abs(e.dragDeltaY);
  617.     },
  618.  
  619.     /**
  620.      * On each drag move event reposition the container appropriately so the
  621.      * cards look like they are sliding.
  622.      * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event.
  623.      * @private
  624.      */
  625.     onDragMove_: function(e) {
  626.       var deltaX = e.dragDeltaX;
  627.       // If dragging beyond the first or last card then apply a backoff so the
  628.       // dragging feels stickier than usual.
  629.       if (!this.currentCard && deltaX > 0 ||
  630.           this.currentCard == (this.cards_.length - 1) && deltaX < 0) {
  631.         deltaX /= 2;
  632.       }
  633.       this.translateTo_(this.currentLeft_ + deltaX);
  634.     },
  635.  
  636.     /**
  637.      * On drag end events we may want to transition to another card, depending
  638.      * on the ending position of the drag and the velocity of the drag.
  639.      * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event.
  640.      * @private
  641.      */
  642.     onDragEnd_: function(e) {
  643.       var deltaX = e.dragDeltaX;
  644.       var velocity = this.touchHandler_.getEndVelocity().x;
  645.       var newX = this.currentLeft_ + deltaX;
  646.       var newCardIndex = Math.round(-newX / this.cardWidth_);
  647.  
  648.       if (newCardIndex == this.currentCard && Math.abs(velocity) >
  649.           CardSlider.TRANSITION_VELOCITY_THRESHOLD_) {
  650.         // The drag wasn't far enough to change cards but the velocity was
  651.         // high enough to transition anyways. If the velocity is to the left
  652.         // (negative) then the user wishes to go right (card + 1).
  653.         newCardIndex += velocity > 0 ? -1 : 1;
  654.       }
  655.       // Ensure that the new card index is valid.  The new card index could be
  656.       // invalid if a swipe suggests scrolling off the end of the list of
  657.       // cards.
  658.       if (newCardIndex < 0)
  659.         newCardIndex = 0;
  660.       else if (newCardIndex >= this.cardCount)
  661.         newCardIndex = this.cardCount - 1;
  662.       this.selectCard(newCardIndex, /* animate */ true);
  663.     },
  664.  
  665.     /**
  666.      * Cancel any current touch/slide as if we saw a touch end
  667.      */
  668.     cancelTouch: function() {
  669.       // Stop listening to any current touch
  670.       this.touchHandler_.cancelTouch();
  671.  
  672.       // Ensure we're at a card bounary
  673.       this.transformToCurrentCard_(true);
  674.     },
  675.   };
  676.  
  677.   return {
  678.     CardSlider: CardSlider
  679.   };
  680. });
  681.